package com.uwlaser.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.uwlaser.base.PageInfo;
import com.uwlaser.base.QueryHelpMybatisPlus;
import com.uwlaser.base.impl.CommonServiceImpl;
import com.uwlaser.domain.ProductCoding;
import com.uwlaser.domain.ProductCodingDateConvert;
import com.uwlaser.domain.ProductCodingRule;
import com.uwlaser.service.ProductCodingRuleService;
import com.uwlaser.service.dto.ProductCodingDateDto;
import com.uwlaser.service.dto.ProductCodingRuleDto;
import com.uwlaser.service.dto.ProductCodingRuleQueryParam;
import com.uwlaser.service.dto.ProductCodingSeriesDto;
import com.uwlaser.service.mapper.ProductCodingDateConvertMapper;
import com.uwlaser.service.mapper.ProductCodingMapper;
import com.uwlaser.service.mapper.ProductCodingRuleMapper;
import com.uwlaser.utils.ConvertUtil;
import com.uwlaser.utils.DateUtil;
import com.uwlaser.utils.PageUtil;
import com.uwlaser.utils.enums.DictValueEnum;
import lombok.AllArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.text.SimpleDateFormat;
import java.util.*;

/**
* @author zk-Qin
* @date 2022-05-11
*/
@Service
@AllArgsConstructor
// @CacheConfig(cacheNames = ProductCodingRuleService.CACHE_KEY)
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class)
public class ProductCodingRuleServiceImpl extends CommonServiceImpl<ProductCodingRuleMapper, ProductCodingRule> implements ProductCodingRuleService {

    // private final RedisUtils redisUtils;
    private final ProductCodingRuleMapper productCodingRuleMapper;

    @Autowired
    private final ProductCodingDateConvertMapper productCodingDateConvertMapper;

    @Autowired
    private final ProductCodingMapper productCodingMapper;

    @Override
    public PageInfo<ProductCodingRuleDto> queryAll(ProductCodingRuleQueryParam query, Pageable pageable) {
        IPage<ProductCodingRule> queryPage = PageUtil.toMybatisPage(pageable);
        IPage<ProductCodingRule> page = productCodingRuleMapper.selectPage(queryPage, QueryHelpMybatisPlus.getPredicate(query));
        return ConvertUtil.convertPage(page, ProductCodingRuleDto.class);
    }

    @Override
    public List<ProductCodingRuleDto> queryAll(ProductCodingRuleQueryParam query){
        return ConvertUtil.convertList(productCodingRuleMapper.selectList(QueryHelpMybatisPlus.getPredicate(query)), ProductCodingRuleDto.class);
    }

    @Override
    public ProductCodingRule getById(Integer id) {
        return productCodingRuleMapper.selectById(id);
    }

    @Override
    // @Cacheable(key = "'id:' + #p0")
    public ProductCodingRuleDto findById(Integer id) {
        return ConvertUtil.convert(getById(id), ProductCodingRuleDto.class);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int insert(ProductCodingRuleDto resources) throws Exception {
        ruleToJson(resources);
        setFinalContent(resources);
        ProductCodingRule entity = ConvertUtil.convert(resources, ProductCodingRule.class);
        productCodingRuleMapper.insert(entity);
        return editCodingExample(entity);
    }

    /**
     * 新增/修改时，finalContent跟着变
     * @param resources ProductCodingRuleDto
     * @throws Exception 异常
     */
    private void setFinalContent(ProductCodingRuleDto resources) throws Exception {
        String ruleType = resources.getRuleType();
        if(DictValueEnum.RULE_TYPE_GD.getValue().equals(ruleType)){
            //固定
            resources.setFinalContent(resources.getRuleContent());
        } else if(DictValueEnum.RULE_TYPE_DATE.getValue().equals(ruleType)){
            //日期
            try {
                String finalContent = getDateTimeByRule(ConvertUtil.convert(resources, ProductCodingRule.class));
                resources.setFinalContent(finalContent);
            } catch (Exception e) {
                log.error("日期规则JSON异常：", e);
                throw new RuntimeException("日期规则JSON异常");
            }
        } else if(DictValueEnum.RULE_TYPE_SQL.getValue().equals(ruleType)){
            //SQL
            String finalContent = null;
            try {
                finalContent = getSqlByRule(ConvertUtil.convert(resources, ProductCodingRule.class));
                resources.setFinalContent(finalContent);
            } catch (Exception e) {
                log.error("SQL规则设置执行异常：", e);
                throw new RuntimeException("SQL语句错误！");
            }
        }else if(DictValueEnum.RULE_TYPE_NUM.getValue().equals(ruleType)){
            //序列号
            try {
                String finalContent = getTempAutoSnByRule(ConvertUtil.convert(resources, ProductCodingRule.class));
                resources.setFinalContent(finalContent);
            } catch (Exception e) {
                log.error("序列号规则JSON异常：", e);
                throw new RuntimeException("序列号规则JSON异常");
            }
        }else if(DictValueEnum.RULE_TYPE_OTHER.getValue().equals(ruleType)){
            //其他
            String finalContent = getOtherByRule(ConvertUtil.convert(resources, ProductCodingRule.class));
            resources.setFinalContent(finalContent);
        }else{
            resources.setFinalContent(resources.getRuleContent());
        }
    }

    /**
     * 临时序列号
     * @param codingRule
     * @return
     */
    private String getTempAutoSnByRule(ProductCodingRule codingRule) throws Exception {
        String ruleJson = codingRule.getRuleJson();
        ProductCodingSeriesDto codingSeries = JSONObject.parseObject(ruleJson,ProductCodingSeriesDto.class);
        if (codingSeries == null) {
            return "";
        }
        //自增序列号
        Integer autoSN = codingSeries.getSeriesInit() == null ? 1 : codingSeries.getSeriesInit() + 1;
        //长度
        Integer length = codingSeries.getSeriesLength();

        //循环规则
        if (codingSeries.getIsLoog().equals(DictValueEnum.YES.getValue())) {
            //上一次重置序列号时间
            Date upDate = new Date(codingSeries.getTempUpdateTime() == null ? new Date().getTime() : codingSeries.getTempUpdateTime());
            //当前时间
            String currTime;
            //上一次重重置时间
            String upTime;
            //转换格式
            String format;
            //y：年，m：月，d：日
            switch (codingSeries.getBasic()) {
                case "0":
                    format = "yyyy";
                    break;
                case "1":
                    format = "MM";
                    break;
                case "2":
                    format = "dd";
                    break;
                default:
                    format = "dd";
                    break;
            }
            currTime = DateUtil.getCurrentDate(format);
            upTime = DateUtil.getDateFormat(upDate, format);
            //如果时间不一样重置序列号
            if (codingSeries.getTempUpdateTime() == null || !currTime.equals(upTime)) {
                autoSN = 1; //重置起始序号：1
                codingSeries.setTempUpdateTime(new Date().getTime());//重置时间
            }

        }
        //补充 0
        String prefix = "";
        int n = length - (autoSN.toString().length());
        for (int i = 0; i < n; i++) {
            prefix += "0";
        }
        //更新规则
        codingSeries.setTempSeriesInit(autoSN);
        return prefix + autoSN;
    }

    /**
     * SQL
     * @param codingRule
     * @return
     */
    private String getSqlByRule(ProductCodingRule codingRule) throws Exception {

        return productCodingRuleMapper.getSqlByRule(codingRule.getRuleJson());
    }

    /**
     * 日期
     * @param codingRule
     * @return
     */
    private String getDateTimeByRule(ProductCodingRule codingRule) throws Exception {

        String ruleJson = codingRule.getRuleJson();
        ProductCodingDateDto codingDate = JSONObject.parseObject(ruleJson, ProductCodingDateDto.class);
        if (codingDate == null) {
            return "";
        }
        String dateFormat = codingDate.getDateFormat();
        //判断是否开启替换
        if (codingDate.getIsConvert().equals(DictValueEnum.YES.getValue())) {
            //获取转换表
            List<ProductCodingDateConvert> converts = productCodingDateConvertMapper.selectList(null);
            //替换规则
            HashMap<String, String> formatMap = new HashMap<String, String>();
            formatMap.put("yy", "Y");
            formatMap.put("yyyy", "Y");
            formatMap.put("MM", "M");
            formatMap.put("dd", "D");
            formatMap.put("HH", "H");
            for (String key : formatMap.keySet()) {
                if (key.contains(dateFormat)) {
                    SimpleDateFormat sf = new SimpleDateFormat(dateFormat);
                    String date = sf.format(new Date());
                    String type = formatMap.get(key);
                    String code = convertDate(converts, date, type);
                    if (code == null) {
                        dateFormat = dateFormat.replaceAll(key, date);
                    } else {
                        dateFormat = dateFormat.replaceAll(key, code);
                    }
                }
            }
            return dateFormat;
        } else {
            //普通规则
            SimpleDateFormat format = new SimpleDateFormat(dateFormat);
            return format.format(new Date());
        }
    }

    /**
     * 转换日期代码
     *
     * @param dateListAll 转换列表
     * @param dateValue   日期值
     * @param dateType    类型
     * @return
     */
    public String convertDate(List<ProductCodingDateConvert> dateListAll, String dateValue, String dateType) {
        for (ProductCodingDateConvert code : dateListAll) {
            if (code.getDateValue().equals(dateValue) && code.getDateType().equals(dateType)) {
                return code.getConvertValue();
            }
        }
        return null;
    }

    /**
     * 其他自定义
     * @param codingRule
     * @return
     */
    private String getOtherByRule(ProductCodingRule codingRule) {
        return "";
    }

    /**
     * 将日期、SQL、序列号对象转成JSON存入ruleJson字段和ruleContent字段
     * @param resources ProductCodingRuleDto
     * @throws JsonProcessingException json转换异常
     */
    private void ruleToJson(ProductCodingRuleDto resources) throws JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper();
        String ruleType = resources.getRuleType();
        if (DictValueEnum.RULE_TYPE_DATE.getValue().equals(ruleType)) {
            if (resources.getCodingDate() != null) {
                ProductCodingDateDto codingDate = resources.getCodingDate();
                String json = mapper.writeValueAsString(codingDate);
                resources.setRuleJson(json);
                resources.setRuleContent(codingDate.getDateFormat());
            }
        } else if (DictValueEnum.RULE_TYPE_SQL.getValue().equals(ruleType)) {
            resources.setRuleJson(resources.getRuleContent());
        } else if (DictValueEnum.RULE_TYPE_NUM.getValue().equals(ruleType)) {
            if (resources.getCodingSeries() != null) {
                ProductCodingSeriesDto codingSeries = resources.getCodingSeries();
                String json = mapper.writeValueAsString(codingSeries);
                resources.setRuleJson(json);
                // 转换成：[n ~ xxx]
                StringBuilder builder = new StringBuilder("[");
                builder.append(codingSeries.getSeriesInit()).append(" ~ ");
                Integer length = codingSeries.getSeriesLength();
                for (int i = 0; i < length; i++) {
                    builder.append("x");
                }
                builder.append("]");
                resources.setRuleContent(builder.toString());
            }
        }
    }

    /**
     * 每个规则新增/修改，都要更新ProductCoding中的codingExample字段
     * @param entity
     * @return
     * @throws Exception
     */
    private int editCodingExample(ProductCodingRule entity) throws Exception {
        if (entity.getCodingId() != null) {
            ProductCodingRuleQueryParam queryParam = new ProductCodingRuleQueryParam();
            queryParam.setCodingId(entity.getCodingId());
            List<ProductCodingRuleDto> ruleDtoList = ConvertUtil.convertList(
                    productCodingRuleMapper.selectList(QueryHelpMybatisPlus.getPredicate(queryParam)),
                    ProductCodingRuleDto.class);
            StringBuilder builder = new StringBuilder();
            ruleDtoList.stream().sorted(Comparator.comparing(ProductCodingRuleDto::getRuleSort))
                    .forEach(e -> builder.append(e.getFinalContent()));
            ProductCoding productCoding = ProductCoding.builder().id(entity.getCodingId())
                    .codingExample(builder.toString()).build();
            return productCodingMapper.updateById(productCoding);
        } else {
            return -1;
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int updateById(ProductCodingRuleDto resources) throws Exception {
        ruleToJson(resources);
        setFinalContent(resources);
        ProductCodingRule entity = ConvertUtil.convert(resources, ProductCodingRule.class);
        productCodingRuleMapper.updateById(entity);
        // delCaches(resources.id);
        return editCodingExample(entity);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int removeByIds(Set<Integer> ids){
        // delCaches(ids);
        return productCodingRuleMapper.deleteBatchIds(ids);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int removeById(Integer id){
        Set<Integer> set = new HashSet<>(1);
        set.add(id);
        return this.removeByIds(set);
    }

    /*
    private void delCaches(Integer id) {
        redisUtils.delByKey(CACHE_KEY + "::id:", id);
    }

    private void delCaches(Set<Integer> ids) {
        for (Integer id: ids) {
            delCaches(id);
        }
    }*/

    /*
    @Override
    public void download(List<ProductCodingRuleDto> all, HttpServletResponse response) throws IOException {
      List<Map<String, Object>> list = new ArrayList<>();
      for (ProductCodingRuleDto productCodingRule : all) {
        Map<String,Object> map = new LinkedHashMap<>();
              map.put("编码规则ID", productCodingRule.getCodingId());
              map.put("规则类型", productCodingRule.getRuleType());
              map.put("规则顺序", productCodingRule.getRuleSort());
              map.put("规则内容", productCodingRule.getRuleContent());
              map.put("生成的内容", productCodingRule.getFinalContent());
              map.put("规则JSON", productCodingRule.getRuleJson());
              map.put("描述", productCodingRule.getDescribes());
        list.add(map);
      }
      FileUtil.downloadExcel(list, response);
    }*/
}
